package result

import (
	"errors"
	"go/ast"
	"property_api/internal/types"
	"property_api/utils/iface"
	"reflect"
	"strings"
)

type Result struct {
	Message string
	Code    int32
	Data    interface{}
	Count   int64
	List    interface{}
}

func Err(mes error) iface.IErr {
	message := "rpc error: code = Unknown desc ="
	Err := mes.Error()
	return &Result{
		Message: func() string {
			if index := strings.Index(Err, message); index != -1 {
				return Err[index+len(message)+1:]
			}
			return Err
		}(),
		Code: 1,
		Data: nil,
	}
}

func Suc(any interface{}) iface.ISuc {
	if any == nil {
		return nil
	}
	result := new(Result)
	val := reflect.Indirect(reflect.ValueOf(any))
	if !val.IsValid() {
		return nil
	}
	resVal := reflect.ValueOf(result).Elem()
	for i, fields := 0, val.Type(); i < fields.NumField(); i++ {
		fieldTyp := fields.Field(i)
		fieldTag, exist := fieldTyp.Tag.Lookup("json")
		if !exist || !ast.IsExported(fieldTyp.Name) {
			continue
		}
		fieldTag = strings.Split(fieldTag, ",")[0]
		resField := resVal.FieldByName(fieldTag)
		if resField.IsValid() && resField.CanSet() {
			resField.Set(val.Field(i))
		}
	}
	return result
}

func SucPage(any interface{}, typ interface{}, handle ...func(value interface{}) interface{}) iface.ISuc {
	if any == nil {
		return nil
	}
	result := new(Result)
	resVal := reflect.ValueOf(result).Elem()
	val := reflect.Indirect(reflect.ValueOf(any))
	if !val.IsValid() {
		return nil
	}
	for i, fields := 0, val.Type(); i < fields.NumField(); i++ {
		fieldTyp := fields.Field(i)
		fieldTag, exist := fieldTyp.Tag.Lookup("json")
		if !exist || !ast.IsExported(fieldTyp.Name) {
			continue
		}
		fieldTag = strings.Split(fieldTag, ",")[0]

		if strings.ToLower(fieldTag) == "list" {
			result.List = copyList(val.Field(i), reflect.TypeOf(typ))
			continue
		}
		resField := resVal.FieldByName(fieldTag)
		if resField.IsValid() && resField.CanSet() && !val.IsZero() {
			resField.Set(val.Field(i))
		}
	}
	if len(handle) > 0 {
		result.List = handle[0](result.List)
	}
	return result
}

func (r *Result) End() (*types.Response, error) {
	return &types.Response{
		Message: r.Message,
		Code:    r.Code,
		Data:    r.Data,
	}, nil
}

func (r *Result) PageEd() (*types.PageRes, error) {
	return &types.PageRes{
		Count: r.Count,
		List:  r.List,
	}, nil
}

func (r *Result) ErrEd() (*types.PageRes, error) {
	return nil, errors.New(r.Message)
}

func copyList(value reflect.Value, typ reflect.Type) interface{} {
	// Check if the value is a valid slice
	if value.Kind() != reflect.Slice {
		return reflect.Value{}
	}

	// If there is no data (empty slice), return an empty slice
	if value.Len() == 0 {
		return reflect.MakeSlice(reflect.SliceOf(reflect.PtrTo(typ)), 0, 0).Interface()
	}

	// Determine the element type (handle pointer cases)
	element := typ
	if element.Kind() == reflect.Ptr {
		element = typ.Elem()
	}

	// Create a slice of pointers to the element type
	result := reflect.MakeSlice(reflect.SliceOf(reflect.PtrTo(element)), value.Len(), value.Len())

	// Iterate through each element of the input slice
	for i := 0; i < value.Len(); i++ {
		obj := value.Index(i)
		dist := result.Index(i)

		// Dereference pointers if the current element is a pointer
		if obj.Kind() == reflect.Ptr {
			obj = obj.Elem()
		}

		// Create a new instance of the element type
		dist.Set(reflect.New(element))
		dist = dist.Elem()

		// Copy fields from obj to dist
		for j := 0; j < obj.NumField(); j++ {
			fieldTyp := obj.Type().Field(j)

			// Skip non-exported fields or fields without a json tag
			fieldTag, exist := fieldTyp.Tag.Lookup("json")
			if !exist || !ast.IsExported(fieldTyp.Name) {
				continue
			}

			// Only take the first part of the json tag (ignoring options like `omitempty`)
			fieldTag = strings.Split(fieldTag, ",")[0]

			// Find the corresponding field in dist
			resField := dist.FieldByName(fieldTag)
			if !resField.IsValid() || !resField.CanSet() {
				continue
			}

			// Get the source field value and ensure the types match
			srcFieldValue := obj.Field(j)
			dstFieldType := resField.Type()

			// Convert the type if necessary
			if srcFieldValue.Type() != dstFieldType {
				srcFieldValue = srcFieldValue.Convert(dstFieldType)
			}

			// Set the value in the destination field
			resField.Set(srcFieldValue)
		}
	}

	// Return the result slice as an interface
	return result.Interface()
}
